AWS SAMでAPI GatewayのIAM認証をしてみた
API GatewayとLambdaで作るAPIがあります。本記事では、このAPIにIAM認証を適用してみました。 これらの仕組みをAWS SAMで作成します。
おすすめの方
- AWS SAMでAPI GatewayのIAM認証をしたい方
- AWS SAMでAPI(API Gateway)を作りたい方
- AWS SAMでOpen APIを使いたい方
AWS SAMでAPIを作成する
sam init
sam init \ --runtime python3.9 \ --name Api-IAM-Sample \ --app-template hello-world \ --package-type Zip
OpenAPI定義
GET /hello
を作成します。
openapi: 3.0.1 info: title: sample api version: 1.0.0 paths: /hello: get: tags: - hello responses: 200: $ref: "#/components/responses/200" 403: $ref: "#/components/responses/403" 500: $ref: "#/components/responses/500" security: - awsSigv4: [] x-amazon-apigateway-integration: type: aws_proxy uri: 'Fn::Sub': >- arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HelloWorldFunction.Arn}/invocations httpMethod: POST responses: default: statusCode: 200 passthroughBehavior: when_no_templates contentHandling: CONVERT_TO_TEXT components: securitySchemes: awsSigv4: type: apiKey name: Authorization in: header x-amazon-apigateway-authtype: awsSigv4 responses: 200: description: Success 403: description: User or client is not authorized. 500: description: Internal Server Error
SAMテンプレート
次のリソースを定義しています。
- IAMユーザ
- IAMユーザ用のIAMポリシー
- API Gateway
- Lambda
- Lambda用のロググループ
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Api-IAM-Sample Parameters: StageName: Type: String Default: v1 Resources: # IAM認証で使うIAMユーザ(アクセスキーを使う) IamUser: Type: AWS::IAM::User Properties: UserName: !Sub api-gateway-user IamUserPolicy: Type: AWS::IAM::Policy Properties: PolicyName: !Sub api-gateway-user-policy PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: execute-api:Invoke Resource: !Sub arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${HelloWorldApi}/${StageName}/GET/hello Users: - !Ref IamUser HelloWorldApi: Type: AWS::Serverless::Api Properties: StageName: !Ref StageName Auth: DefaultAuthorizer: AWS_IAM DefinitionBody: Fn::Transform: Name: AWS::Include Parameters: Location: s3://cm-fujii.genki-deploy/Api-IAM-Sample-Stack/api.yaml HelloWorldFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello_world/ Handler: app.lambda_handler Runtime: python3.9 Timeout: 3 Architectures: - x86_64 Events: HelloWorld: Type: Api Properties: Path: /hello Method: get RestApiId: !Ref HelloWorldApi HelloWorldFunctionLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub /aws/lambda/${HelloWorldFunction} Outputs: HelloWorldApi: Value: !Sub "https://${HelloWorldApi}.execute-api.${AWS::Region}.amazonaws.com/${StageName}/hello"
Lambdaコード
import json def lambda_handler(event, context): return { "statusCode": 200, "body": json.dumps({ "message": "hello world", }), }
デプロイ
aws s3 cp \ api.yaml \ s3://cm-fujii.genki-deploy/Api-IAM-Sample-Stack/api.yaml sam build sam package \ --output-template-file packaged.yaml \ --s3-bucket cm-fujii.genki-deploy sam deploy \ --template-file packaged.yaml \ --stack-name Api-IAM-Sample-Stack \ --s3-bucket cm-fujii.genki-deploy \ --capabilities CAPABILITY_NAMED_IAM \ --no-fail-on-empty-changeset
エンドポイント取得
$ aws cloudformation describe-stacks \ --stack-name Api-IAM-Sample-Stack \ --query 'Stacks[].Outputs' [ [ { "OutputKey": "HelloWorldApi", "OutputValue": "https://xxx.execute-api.ap-northeast-1.amazonaws.com/v1/hello" } ] ]
APIにアクセスしてみる(失敗する)
SigV4署名ヘッダが無いので、アクセスに失敗します。
$ curl https://xxx.execute-api.ap-northeast-1.amazonaws.com/v1/hello {"message":"Missing Authentication Token"}
SigV4署名ヘッダを使って、APIにアクセスする
アクセスキーを取得する
下記コマンドでアクセスキーを取得します。マネジメントコンソールから取得してもOKです。
aws iam create-access-key \ --user-name api-gateway-user
APIアクセス用のスクリプトを作成する
Postmanを使えば簡単に動作確認できますが、今回は、Pythonを使ってアクセスしてみます。
次のライブラリを利用します。
- Requests: HTTP for Humans™ — Requests 2.27.1 documentation
- tedder/requests-aws4auth: Amazon Web Services version 4 authentication for the Python Requests module
pip install requests pip install requests-aws4auth
スクリプト本体は次のコードです。さきほど取得したAWSアクセスキーを利用します。
import requests from requests_aws4auth import AWS4Auth API_ENDPOINT = 'https://xxx.execute-api.ap-northeast-1.amazonaws.com/v1/hello' auth = AWS4Auth( 'your access key', 'your secret key', 'ap-northeast-1', 'execute-api', ) response = requests.get(API_ENDPOINT, auth=auth) print(response.status_code) print(response.json())
APIにアクセスする
成功しました!
$ python sample.py 200 {'message': 'hello world'}
参考
- AWS::Serverless::Api - AWS Serverless Application Model
- ApiAuth - AWS Serverless Application Model
- API を呼び出すためのアクセスの制御 - Amazon API Gateway
- 署名バージョン 4 を使用した AWS リクエストへの署名 - AWS 全般のリファレンス
- API Gateway REST API の IAM 認証を有効にする
- AWS API Gateway | Postman Learning Center
- tedder/requests-aws4auth: Amazon Web Services version 4 authentication for the Python Requests module
- x amazon-apigateway-authtype プロパティ - Amazon API Gateway
- [OpenAPI] AWS SAMでLambdaオーソライザーを「適用するLambda」と「適用しないAPI」を作ってみた | DevelopersIO